﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Sakura.Silverlight.Extensions
{
	/// <summary>
	/// 提供一种在所有单元格大小相同的网格中安排内容的方法。
	/// </summary>
	public class UniformGrid : Panel
	{
		/// <summary>
		/// 初始化容器的一个新实例。
		/// </summary>
		public UniformGrid()
		{
		}

		#region 公共属性

		/// <summary>
		/// 获取或设置网格中的列数。
		/// </summary>
		public int Columns
		{
			get
			{
				return (int)GetValue(ColumnsProperty);
			}
			set
			{
				SetValue(ColumnsProperty, value);
			}
		}

		/// <summary>
		/// 获取或设置网格中的行数。
		/// </summary>
		public int Rows
		{
			get
			{
				return (int)GetValue(RowsProperty);
			}
			set
			{
				SetValue(RowsProperty, value);
			}
		}

		/// <summary>
		/// 获取或设置网格中元素的排列方向。
		/// </summary>
		public Orientation Orientation
		{
			get
			{
				return (Orientation)GetValue(OrientationProperty);
			}
			set
			{
				SetValue(OrientationProperty, value);
			}
		}


		/// <summary>
		/// 获取或设置第一个元素相在单元格中序列的位置。默认值为 0。
		/// </summary>
		/// <remarks>
		/// 如果该数值大于 0，则第一个单元格之前将留出响应数量的空单元格。如果该数值小于 0，则第一个单元格显示的内容将跳过响应数量的子元素。
		/// </remarks>
		public int FirstOffest
		{
			get
			{
				return (int)GetValue(FirstOffestProperty);
			}
			set
			{
				SetValue(FirstOffestProperty, value);
			}
		}

		#endregion 公共属性

		#region 依赖属性字段

		/// <summary>
		/// 标识 <see cref="Columns"/> 依赖项属性。
		/// </summary>
		public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(UniformGrid), new PropertyMetadata(1, ColumnsPropertyChangedCallback));

		/// <summary>
		/// 标识 <see cref="Rows"/> 依赖项属性。
		/// </summary>
		public static readonly DependencyProperty RowsProperty = DependencyProperty.Register("Rows", typeof(int), typeof(UniformGrid), new PropertyMetadata(1, RowsPropertyChangedCallback));

		/// <summary>
		/// 标识 <see cref="Orientation"/> 依赖项属性。
		/// </summary>
		public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(UniformGrid), new PropertyMetadata(Orientation.Horizontal, OrientationPropertyChangedCallback));

		/// <summary>
		/// 标识 <see cref="FirstOffest"/> 依赖项属性。
		/// </summary>
		public static readonly DependencyProperty FirstOffestProperty = DependencyProperty.Register("FirstOffest", typeof(int), typeof(UniformGrid), new PropertyMetadata(0, FirstOffestPropertyChangedCallback));

		#region 回调

		//	public delegate void PropertyChangedCallback(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
		//System.Windows 的成员


		private static void ColumnsPropertyChangedCallback(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
		{
			if ((int)e.NewValue < 1)
			{
				throw new ArgumentOutOfRangeException("value", "Columns 必须至少为 1。");
			}

			var me = (UniformGrid)d;
			me.InvalidateMeasure();
		}

		private static void RowsPropertyChangedCallback(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
		{
			if ((int)e.NewValue < 1)
			{
				throw new ArgumentOutOfRangeException("value", "Rows 必须至少为 1。");
			}

			var me = (UniformGrid)d;
			me.InvalidateMeasure();
		}

		private static void OrientationPropertyChangedCallback(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
		{
			switch ((Orientation)e.NewValue)
			{
				case Orientation.Horizontal:
				case Orientation.Vertical:
					break;
				default:
					throw new ArgumentException("Orentation 属性不是有效的值。", "value");
			}

			var me = (UniformGrid)d;
			me.InvalidateArrange();
		}

		private static void FirstOffestPropertyChangedCallback(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
		{
			var me = (UniformGrid)d;
			me.InvalidateArrange();
		}



		#endregion 回调


		#endregion 依赖属性字段

		#region 内部布局方法

		/// <summary>
		/// 测量子元素在布局中所需的大小，然后确定派生类的大小。
		/// </summary>
		/// <param name="availableSize">此元素可以赋给子元素的可用大小。</param>
		/// <returns>此元素在布局过程中所需的大小，这是由此元素根据对其子元素大小的计算而确定的。</returns>
		protected override Size MeasureOverride(Size availableSize)
		{
			var elementSize = new Size(availableSize.Width / this.Columns, availableSize.Height / this.Rows);

			foreach (var i in this.Children)
			{
				i.Measure(elementSize);
			}

			return base.MeasureOverride(availableSize);
		}

		/// <summary>
		/// 定位子元素并确定派生类的大小。
		/// </summary>
		/// <param name="finalSize">父级中此元素应用来排列自身及其子元素的最终区域。</param>
		/// <returns>所用的实际大小。</returns>
		protected override Size ArrangeOverride(Size finalSize)
		{
			var elementWidth = finalSize.Width / this.Columns;
			var elementHeight = finalSize.Height / this.Rows;

			for (var i = 0; i < this.Children.Count; i++)
			{
				var element = this.Children[i];

				int realRow, realColumn;

				if (!this.GetElementLocation(i, out realRow, out realColumn))
				{
					System.Diagnostics.Debug.WriteLine("警告，子元素 {0} 超过元素显示范围，已经重置到 ({1},{2}) 单元格位置。", i, realRow, realColumn);
				}

				var region = new Rect
				{
					X = realColumn * elementWidth,
					Y = realRow * elementHeight,
					Width = elementWidth,
					Height = elementHeight
				};

				element.Arrange(region);

			}

			return base.ArrangeOverride(finalSize);
		}

		#endregion 内部布局方法

		#region 辅助方法

		/// <summary>
		/// 获得指定编号的子元素在面板中的位置。
		/// </summary>
		/// <param name="index">要获得位置的子元素在集合中的索引。</param>
		/// <param name="row">子元素的实际行号。</param>
		/// <param name="column">子元素的实际列号。</param>
		/// <returns>表示元素是否超过了容器边界。</returns>
		private bool GetElementLocation(int index, out int row, out int column)
		{
			var realIndex = index + this.FirstOffest;

			// 如果越界的话则返回 -1
			if (realIndex >= this.Rows * this.Columns)
			{
				row = this.Rows - 1;
				column = this.Columns - 1;
				return false;
			}
			else if (realIndex < 0)
			{
				row = 0;
				column = 0;
				return false;
			}

			switch (this.Orientation)
			{
				case System.Windows.Controls.Orientation.Horizontal:
					row = realIndex / this.Columns;
					column = realIndex - (row * this.Columns);
					return true;
				case System.Windows.Controls.Orientation.Vertical:
					column = realIndex / this.Rows;
					row = realIndex - (column * this.Rows);
					return true;
				// 无效方向（此情况不会发生）。
				default:
					row = -1;
					column = -1;
					return false;
			}
		}

		#endregion 辅助方法
	}
}
